package youdu

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"sync"
	"time"
)

type tokenInfo struct {
	accessToken string
	expireTime  time.Time
}

type tokenCache struct {
	tokens map[string]*tokenInfo
	mutex  sync.RWMutex
}

type tokenRequest struct {
	Buin    int    `json:"buin"`
	AppId   string `json:"appId"`
	Encrypt string `json:"encrypt"`
}

type tokenResponse struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
	Encrypt string `json:"encrypt"`
}

type tokenData struct {
	AccessToken string `json:"accessToken"`
	ExpireIn    int    `json:"expireIn"`
}

var (
	tokenStore = &tokenCache{
		tokens: make(map[string]*tokenInfo),
	}
)

// generateCacheKey 生成缓存key
func generateCacheKey(server string, buin int, appId string) string {
	return fmt.Sprintf("%s_%d_%s", server, buin, appId)
}

// GetAccessToken 获取有度服务器访问凭证
// 如果凭证在有效期内，直接返回缓存的凭证
// 如果凭证过期或未缓存，则重新获取
func (o *tools) GetAccessToken(apiServer string, buin int, appId string, encodedAesKey string) (string, error) {
	cacheKey := generateCacheKey(apiServer, buin, appId)

	tokenStore.mutex.RLock()
	if token, exists := tokenStore.tokens[cacheKey]; exists && token.accessToken != "" && time.Now().Before(token.expireTime) {
		defer tokenStore.mutex.RUnlock()
		return token.accessToken, nil
	}
	tokenStore.mutex.RUnlock()

	// 需要重新获取token
	tokenStore.mutex.Lock()
	defer tokenStore.mutex.Unlock()

	// 双重检查，防止在等待锁的过程中其他goroutine已经更新了token
	if token, exists := tokenStore.tokens[cacheKey]; exists && token.accessToken != "" && time.Now().Before(token.expireTime) {
		return token.accessToken, nil
	}

	// 解码AES密钥
	aesKey, err := Tools.DecodeAESKey(encodedAesKey)
	if err != nil {
		return "", err
	}

	// 获取当前Unix时间戳
	timestamp := fmt.Sprintf("%d", time.Now().Unix())

	// 加密时间戳
	encrypt, err := Tools.AesEncrypt([]byte(timestamp), aesKey, appId)
	if err != nil {
		return "", fmt.Errorf("加密请求数据失败: %v", err)
	}

	// 构造请求体
	reqBody := tokenRequest{
		Buin:    buin,
		AppId:   appId,
		Encrypt: encrypt,
	}

	reqData, err := json.Marshal(reqBody)
	if err != nil {
		return "", fmt.Errorf("序列化请求数据失败: %v", err)
	}

	// 忽略ssl
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{
		Transport: tr,
	}

	// 发送POST请求
	resp, err := client.Post(
		fmt.Sprintf("%s/cgi/gettoken", apiServer),
		"application/json",
		strings.NewReader(string(reqData)),
	)
	if err != nil {
		return "", fmt.Errorf("请求access_token失败: %v", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("读取响应body失败: %v", err)
	}

	var result tokenResponse
	if err := json.Unmarshal(body, &result); err != nil {
		return "", fmt.Errorf("解析响应数据失败: %v", err)
	}

	if result.ErrCode != 0 {
		return "", fmt.Errorf("获取access_token失败: %s", result.ErrMsg)
	}

	// 解密响应数据
	decrypted, err := Tools.AesDecrypt(result.Encrypt, aesKey, appId)
	if err != nil {
		return "", fmt.Errorf("解密响应数据失败: %v", err)
	}

	var tokenResp tokenData
	if err := json.Unmarshal(decrypted.Data, &tokenResp); err != nil {
		return "", fmt.Errorf("解析token数据失败: %v", err)
	}

	// 更新缓存
	tokenStore.tokens[cacheKey] = &tokenInfo{
		accessToken: tokenResp.AccessToken,
		expireTime:  time.Now().Add(time.Duration(tokenResp.ExpireIn-300) * time.Second),
	}

	return tokenResp.AccessToken, nil
}
